/* User authentication for vtysh.
 * Copyright (C) 2000 Kunihiro Ishiguro
 *
 * This file is part of GNU Zebra.
 *
 * GNU Zebra is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * GNU Zebra is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with GNU Zebra; see the file COPYING.  If not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.  
 */

#include <zebra.h>
#include <lib/version.h>

#include <pwd.h>

#ifdef USE_PAM
#include <security/pam_appl.h>
#ifdef HAVE_PAM_MISC_H
#include <security/pam_misc.h>
#endif
#ifdef HAVE_OPENPAM_H
#include <security/openpam.h>
#endif
#endif /* USE_PAM */

#include "memory.h"
#include "linklist.h"
#include "command.h"
#include "vtysh_user.h"

#ifdef USE_PAM
static struct pam_conv conv = 
{
  PAM_CONV_FUNC,
  NULL
};

static int
vtysh_pam (const char *user)
{
  int ret;
  pam_handle_t *pamh = NULL;

  /* Start PAM. */
  ret = pam_start(QUAGGA_PROGNAME, user, &conv, &pamh);
  /* printf ("ret %d\n", ret); */

  /* Is user really user? */
  if (ret == PAM_SUCCESS)
    ret = pam_authenticate (pamh, 0);
  /* printf ("ret %d\n", ret); */
  
#if 0
  /* Permitted access? */
  if (ret == PAM_SUCCESS)
    ret = pam_acct_mgmt (pamh, 0);
  printf ("ret %d\n", ret);

  if (ret == PAM_AUTHINFO_UNAVAIL)
    ret = PAM_SUCCESS;
#endif /* 0 */
  
  /* This is where we have been authorized or not. */
#ifdef DEBUG
  if (ret == PAM_SUCCESS)
    printf("Authenticated\n");
  else
    printf("Not Authenticated\n");
#endif /* DEBUG */

  /* close Linux-PAM */
  if (pam_end (pamh, ret) != PAM_SUCCESS) 
    {
      pamh = NULL;
      fprintf(stderr, "vtysh_pam: failed to release authenticator\n");
      exit(1);
    }

  return ret == PAM_SUCCESS ? 0 : 1;
}
#endif /* USE_PAM */

struct vtysh_user
{
  char *name;
  u_char nopassword;
};

struct list *userlist;

static struct vtysh_user *
user_new ()
{
  return XCALLOC (MTYPE_TMP, sizeof (struct vtysh_user));
}

#if 0
static void
user_free (struct vtysh_user *user)
{
  XFREE (0, user);
}
#endif

static struct vtysh_user *
user_lookup (const char *name)
{
  struct listnode *node, *nnode;
  struct vtysh_user *user;

  for (ALL_LIST_ELEMENTS (userlist, node, nnode, user))
    {
      if (strcmp (user->name, name) == 0)
	return user;
    }
  return NULL;
}

#if 0
static void
user_config_write ()
{
  struct listnode *node, *nnode;
  struct vtysh_user *user;

  for (ALL_LIST_ELEMENTS (userlist, node, nnode, user))
    {
      if (user->nopassword)
	printf (" username %s nopassword\n", user->name);
    }
}
#endif

static struct vtysh_user *
user_get (const char *name)
{
  struct vtysh_user *user;
  user = user_lookup (name);
  if (user)
    return user;

  user = user_new ();
  user->name = strdup (name);
  listnode_add (userlist, user);

  return user;
}

DEFUN (username_nopassword,
       username_nopassword_cmd,
       "username WORD nopassword",
       "\n"
       "\n"
       "\n")
{
  struct vtysh_user *user;
  user = user_get (argv[0]);
  user->nopassword = 1;
  return CMD_SUCCESS;
}

int
vtysh_auth (void)
{
  struct vtysh_user *user;
  struct passwd *passwd;

  if ((passwd = getpwuid (geteuid ())) == NULL)
  {
    fprintf (stderr, "could not lookup user ID %d\n", (int) geteuid());
    exit (1);
  }

  user = user_lookup (passwd->pw_name);
  if (user && user->nopassword)
    /* Pass through */;
  else
    {
#ifdef USE_PAM
      if (vtysh_pam (passwd->pw_name))
	exit (0);
#endif /* USE_PAM */
    }
  return 0;
}

char *
vtysh_get_home (void)
{
  struct passwd *passwd;

  passwd = getpwuid (getuid ());

  return passwd ? passwd->pw_dir : NULL;
}

void
vtysh_user_init (void)
{
  userlist = list_new ();
  install_element (CONFIG_NODE, &username_nopassword_cmd);
}
